using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace RacerGame
{
	internal abstract class Entity
	{
		protected ColObject colObject;
		public ColObject ColObject { get { return colObject; } }
		public FactionEntity FactionEntity;
		
		private bool dead = false;
		public bool Dead { get { return dead; } }
		protected float armor = 1.0f;
		public float Armor { get { return armor; } }
		protected float life = 100;
		public float Life { get { return life; } }

		protected Model model;
		protected Matrix[] transforms;
		public Model Model { get { return model; } }
		public Matrix[] Transforms { get { return transforms; } }
		private Vector3 position;
		public Vector3 Position {
			get { return position; }
			set { position = value; }
		}
		public Vector2 Position2D {
			get { return new Vector2(position.X, position.Z); }
			set { position = new Vector3(value.X, position.Y, value.Y); }
		}
		public Quaternion Rotation;
		private float scale;
		private float scaleFactor = 1.0f;
		public float ScaleFactor {
			get {
				return scaleFactor;
			}
			set {
				scaleFactor = MathHelper.Clamp(value,0,100);
				Scale = Matrix.CreateScale(scale * scaleFactor);
			}
		}
		public Matrix Scale;
		protected float roll = 0.0f;
		public float Roll { get { return roll; } }

		private Matrix rotationMatrix;
		public Matrix RotationMatrix { get { return rotationMatrix; } set { rotationMatrix = value; } }
		private Matrix positionMatrix;
		public Matrix PositionMatrix { get { return positionMatrix; } set { positionMatrix = value; } }

		public Entity(Model model) : this(model, new Vector3(0, 0, 0), Quaternion.Identity, 1.0f) { }
		public Entity(Model model, Vector3 position) : this(model, position, Quaternion.Identity, 1.0f) { }
		public Entity(Model model, Vector3 position, Quaternion rotation) : this(model, position, rotation, 1.0f) { }
		public Entity(Model model, Vector3 position, Quaternion rotation, float scale) {
			this.model = model;
			if( model != null ) {
				this.transforms = getTransforms(model);
			}
			this.Position = position;
			UpdatePositionMatrix();
			this.Rotation = rotation;
			UpdateRotationMatrix();
			this.scale = scale;
			this.Scale = Matrix.CreateScale(scale);
		}

		protected static Matrix[] getTransforms(Model model) {
			Matrix[] transforms = new Matrix[model.Bones.Count];
			model.CopyAbsoluteBoneTransformsTo(transforms);
			return transforms;
		}

		protected void setModel(Model model, Matrix[] transforms){
			this.model = model;
			this.transforms = transforms;
		}

		private void rotateX(float pitch) {
			Rotation *= Quaternion.CreateFromAxisAngle(Vector3.Right, pitch);
		}

		private void rotateY(float yaw) {
			Rotation *= Quaternion.CreateFromAxisAngle(Vector3.Up, yaw);
		}

		private void rotateZ(float roll) {
			this.roll += roll;
			Rotation *= Quaternion.CreateFromAxisAngle(Vector3.Backward, roll);
		}

		public void Rotate(float pitch, float yaw, float roll) {
			rotateX(pitch);
			rotateY(yaw);
			rotateZ(roll);
			UpdateRotationMatrix();
		}
		public void Rotate(Vector3 change) {
			rotateX(change.X);
			rotateY(change.Y);
			rotateZ(change.Z);
			UpdateRotationMatrix();
		}

		public void Move(Vector3 moveVector) {
			Position += moveVector;
			UpdatePositionMatrix();
		}

		public void MoveForwards(float distance) {
			Position += Vector3.Transform(new Vector3(0.0f, 0.0f, -distance), rotationMatrix);
			UpdatePositionMatrix();
		}

		public void MoveBackwards(float distance) {
			Position += Vector3.Transform(new Vector3(0.0f, 0.0f, distance), rotationMatrix);
			UpdatePositionMatrix();
		}

		public void UpdateRotationMatrix() {
			rotationMatrix = Matrix.CreateFromQuaternion(Rotation);
		}

		public void UpdatePositionMatrix() {
			positionMatrix = Matrix.CreateTranslation(Position);
		}

		public void RotateToAngle(Vector3 angle) {
			Matrix matrixRotation = Matrix.CreateLookAt(Vector3.Zero, angle, Vector3.Up);
			matrixRotation = Matrix.Transpose(matrixRotation);
			Rotation = Quaternion.CreateFromRotationMatrix(matrixRotation);
			Rotation *= Quaternion.CreateFromAxisAngle(Vector3.Backward, roll);
			UpdateRotationMatrix();
		}

		public void RotateTowards(Vector3 target) {
			Matrix matrixRotation = Matrix.CreateLookAt(Vector3.Zero, target - Position, Vector3.Up);
			matrixRotation = Matrix.Transpose(matrixRotation);
			Rotation = Quaternion.CreateFromRotationMatrix(matrixRotation);
			Rotation *= Quaternion.CreateFromAxisAngle(Vector3.Backward, roll);
			UpdateRotationMatrix();
		}

		public virtual void DrawColObject(Camera camera) {
			if( colObject != null ) {
				colObject.Draw(camera);
			}
		}

		public virtual void ApplyDamage(float damage, FactionEntity attacker) {
			life -= (damage / armor);
			if( life <= 0 ) {
				life = 0;
				if( !Dead ) {
					FactionEntity.Faction.Dead(FactionEntity, attacker);
				}
				Die();
			}
			FactionEntity.Faction.DamageReport(FactionEntity, attacker, damage);
			updateColObjectColor();
		}

		public virtual void Heal(float life) {
			if( !Dead ) {
				this.life += life;
				if( this.life > 100 ) {
					this.life = 100;
				}
			}
			updateColObjectColor();
		}

		private void updateColObjectColor() {
			if( colObject != null ) {
				colObject.Color = GetLifeColor();
			}
		}

		public Color GetLifeColor() {
			return new Color((byte)(MathHelper.Lerp(0, 255, 1 - Life / 100.0f)), (byte)(MathHelper.Lerp(0, 255, Life / 100.0f)), 0);
		}

		public virtual void Die() {
			if( !dead && !FactionEntity.Faction.Defeated && !RacerGame.Ended ) {
				if( RacerGame.PlaySound ) {
					RacerGame.PlayCue("explode", RacerGame.Listener, FactionEntity.AudioEmitter);
				}
			}
			dead = true;			
			if( colObject != null ) {
				colObject.Die();
			}
		}
	}
}
